
#ifndef __neural_net_geneticH__
#define __neural_net_geneticH__

#include "data.h"
#include <vector>
#include <iostream>

namespace WtfEngine {

	class IGeneticNeuralNetworkImpl {
	public:
		virtual void ProcessInputs( std::vector<float>& vIn ) = 0;
		virtual void ReadOutputs( std::vector<float>& vOut ) = 0;

		virtual void Randomize() = 0;
		virtual void Mutate() = 0;

		virtual void Load(std::istream& s) = 0;
		virtual void Save(std::ostream& s) = 0;

		virtual unsigned GetNeuronCount() const = 0;
		virtual const std::vector<unsigned>& GetInputMap() const = 0;
		virtual const std::vector<unsigned>& GetOutputMap() const = 0;

		virtual float GetDelta( unsigned i ) = 0;
		virtual void BackPropagate( const std::vector<float>& vDesired, float maxError ) = 0;

		virtual unsigned GetSize() const = 0;
		virtual IGeneticNeuralNetworkImpl * Clone() const = 0;
	};

	/**
	 * A neural network implementation based on a serialized weighted connection map
	 * over a fixed pool of neurons, designed for easy mutation and evolution
	 **/
	class GeneticNeuralNetwork: public DataObject {
		IGeneticNeuralNetworkImpl * mpImpl;

		DATA_LOADABLE(GeneticNeuralNetwork, sizeof(GeneticNeuralNetwork)
			+ (mpImpl ? mpImpl->GetSize() : 0));
	public:
		GeneticNeuralNetwork( const StringPool::Entry& name );
		GeneticNeuralNetwork( const StringPool::Entry& name, unsigned dim, const std::vector<unsigned>& inputMap, const std::vector<unsigned>& outputMap );

		void ProcessInputs( std::vector<float>& vIn ) {
			mpImpl->ProcessInputs(vIn);
		};
		void ReadOutputs( std::vector<float>& vOut ) {
			mpImpl->ReadOutputs(vOut);
		};

		void Mutate() { mpImpl->Mutate(); };
		void Randomize() { mpImpl->Randomize(); };

		void SaveToFile(const String& sFile);

		tRef Clone(const StringPool::Entry& name) const;
		void BackPropagate( const std::vector<float>& vDesired, float maxError ) {
			mpImpl->BackPropagate( vDesired, maxError );
		};

		static IDataObject * Load(const StringPool::Entry& sFilePath);
	};

};

#endif
